#!/usr/bin/env python3
# $Id$

from gi.repository import Gtk, Gdk, Pango

import cairo
import sys
import os

DEFAULT_BUFFER = """\
# This is a Python script to quickly test Cairo stuff!
# You have access to the following variables:
# cr = The Cairo drawing context.
# W  = The DrawingArea's allocated width.
# H  = The DrwaingArea's allocated height.

import math

cr.set_line_width(10)
cr.arc(W / 2.0, H / 2.0, H / 3.0, 0.0, 2.0 * math.pi)
cr.set_source_rgb(0.3, 0.3, 0.3)
cr.fill_preserve()
cr.set_source_rgb(0.1, 0.1, 0.1)
cr.stroke()
"""

class Curve(object):
	def __init__(self):
		object.__init__(self)

		self.start = 0, 0
		self.cp0   = 0, 0
		self.cp1   = 0, 0
		self.end   = 0, 0

class CairoDrawingArea(Gtk.DrawingArea):
	def __init__(self, width=0, height=0, win=None, buf=DEFAULT_BUFFER):
		Gtk.DrawingArea.__init__(self)

		self.__buffer = buf
		self.__win    = win
		self.__cp     = []

		self.set_events(
			Gdk.EventMask.BUTTON_PRESS_MASK | 
			Gdk.EventMask.BUTTON_RELEASE_MASK |
			Gdk.EventMask.POINTER_MOTION_MASK |
			Gdk.EventMask.ENTER_NOTIFY_MASK |
			Gdk.EventMask.LEAVE_NOTIFY_MASK
		)

		self.set_size_request(width, height)
		self.connect("motion-notify-event", self.__move)
		self.connect("button-press-event", self.__click)
		self.connect("draw", self.__render)

	def __move(self, widget, event):
		b, bn = event.get_button()

		if b and bn == 3:
			self.__cp.append(Curve)

	def __click(self, widget, event):
		print(event.get_button())

		# print(int(event.x), int(event.y))

	def __render(self, widget, c):
		w = widget.get_allocated_width()
		h = widget.get_allocated_height()

		if self.__win:
			self.__win.set_status(w, h)

		self.__eval(self.__buffer, c, w, h)

		return True

	def __eval(self, buf, cr, w, h):
		eval(compile(buf, "<cairoscript>", "exec"), {
			"cr": cr,
			"W":  w,
			"H":  h
		})

	def __size(self, w, h):
		if not w:
			w = self.get_allocated_width()
		
		if not h:
			h = self.get_allocated_height()
	
		return int(w), int(h)

	def parse(self, buf):
		self.__buffer = buf

		self.queue_draw()

	def write_to_png(self, path, w=None, h=None):
		w, h = self.__size(w, h)
		
		surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
		context = cairo.Context(surface)

		self.__eval(self.__buffer, context, w, h)

		surface.write_to_png(path)

	def write_to_svg(self, path, w=None, h=None):
		w, h = self.__size(w, h)

		surface = cairo.SVGSurface(path, w, h)
		context = cairo.Context(surface)

		self.__eval(self.__buffer, context, w, h)

		surface.flush()
		surface.finish()

class TextView(Gtk.TextView):
	def __init__(self, text=""):
		Gtk.TextView.__init__(self)

		buf = Gtk.TextBuffer()

		buf.set_text(text)

		self.set_buffer(buf)
		self.connect("realize", self.__realize)

	def __realize(self, *args):
		self.set_font_size(10)

	def set_text(self, text):
		self.get_buffer().set_text(text)

	def set_font_size(self, size):
		font = Pango.FontDescription("Droid Sans Mono")

		font.set_size(Pango.SCALE * size)

		self.modify_font(font)

	def get_text(self):
		buf = self.get_buffer()
		
		return buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)

class CairoWindow(Gtk.Window):
	def __init__(self, w, h, script=None, autosave=None):
		Gtk.Window.__init__(self)

		b = script and open(script).read() or DEFAULT_BUFFER

		self.__draw   = CairoDrawingArea(w, h, self, b)
		self.__text   = TextView(b)
		self.__status = Gtk.Statusbar()

		tool   = Gtk.HBox()
		vbox   = Gtk.VBox()
		scroll = Gtk.ScrolledWindow()
		paned  = Gtk.VPaned()
		frame  = Gtk.Frame()

		scroll.add(self.__text)

		frame.add(scroll)

		paned.pack1(self.__draw, False, False)
		paned.pack2(frame, True, True)

		vbox.pack_start(paned, True, True, 0)
		vbox.pack_start(tool, False, False, 2)
		vbox.pack_start(self.__status, False, False, 0)

		for st, tt, cb in zip((
			Gtk.STOCK_OPEN,
			Gtk.STOCK_SAVE,
			Gtk.STOCK_YES,
			Gtk.STOCK_REFRESH,
			Gtk.STOCK_QUIT
		), (
			"Open an existing script",
			"Save this script",
			"Write script to PNG/SVG images",
			"Refresh the DrawingArea",
			"Quit the application"
		), (
			self.__open_script,
			self.__save_script,
			self.__save_image,
			self.__parse,
			lambda *a, **k: Gtk.main_quit()
		)):
			button = Gtk.Button()
			image  = Gtk.Image()

			image.set_from_stock(st, Gtk.IconSize.MENU)

			button.set_relief(Gtk.ReliefStyle.NONE)
			button.set_focus_on_click(False)
			button.set_image(image)
			button.set_tooltip_text(tt)
			button.connect("clicked", cb)
			
			tool.pack_start(button, False, False, 0)

		fontSizeLabel = Gtk.Label("Font Size:")
		fontSize      = Gtk.ComboBoxText()

		fontSizeLabel.set_alignment(1.0, 0.5)

		[fontSize.append_text("%i" % i) for i in range(8, 13)]

		fontSize.connect("changed", self.__set_font)
		fontSize.set_active(2)

		tool.pack_start(fontSizeLabel, True, True, 5)
		tool.pack_start(fontSize, False, False, 0)

		self.set_title("cairodraw")
		self.add(vbox)
		self.set_focus(self.__text)

		if autosave:
			self.__draw.queue_draw()
			self.__save_image(None, w, h)

	def __open_script(self, *args):
		# buttons =
		fc = Gtk.FileChooserDialog(
			"Selet a script file...",
			self,
			Gtk.FileChooserAction.OPEN,
			(
				Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
				Gtk.STOCK_OPEN, Gtk.ResponseType.OK
			)
		)

		fl1 = Gtk.FileFilter()

		fl1.set_name("Pycairo Scripts")
		fl1.add_pattern("*.py")

		fl2 = Gtk.FileFilter()

		fl2.set_name("All Files")
		fl2.add_pattern("*")

		fc.add_filter(fl1)
		fc.add_filter(fl2)

		if fc.run() == Gtk.ResponseType.OK:
			self.__text.set_text(open(fc.get_filename()).read())

			self.__parse()

		fc.destroy()

	def __save_script(self, *args):
		fc = Gtk.FileChooserDialog(
			"Save Python Script as...",
			self,
			Gtk.FileChooserAction.SAVE,
			(
				Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
				Gtk.STOCK_SAVE, Gtk.ResponseType.OK
			)
		)

		if fc.run() == Gtk.ResponseType.OK:
			f = fc.get_filename()

			if not ".py" in f:
				f += ".py"

			open(f, "w").write(self.__text.get_text())

		fc.destroy()

	def __save_image(self, widget, w=None, h=None):
		fc = Gtk.FileChooserDialog(
			"Save PNG & SVG Images as...",
			self,
			Gtk.FileChooserAction.SAVE,
			(
				Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
				Gtk.STOCK_SAVE, Gtk.ResponseType.OK
			)
		)

		if fc.run() == Gtk.ResponseType.OK:
			self.__draw.write_to_png(fc.get_filename() + ".png", w, h)
			self.__draw.write_to_svg(fc.get_filename() + ".svg", w, h)

		fc.destroy()

	def __parse(self, *args):
		self.__draw.parse(self.__text.get_text())
	
	def __set_font(self, cb):
		self.__text.set_font_size(int(cb.get_active_text()))

	def set_status(self, w, h):
		self.__status.push(0, "Dimensions: %i x %i" % (w, h))

if __name__ == "__main__":
	f = None
	w = 512
	h = 256
	s = False

	if len(sys.argv) >= 2 and os.path.exists(sys.argv[1]):
		f = sys.argv[1]

	if len(sys.argv) >= 4:
		try:
			w = int(sys.argv[2])
			h = int(sys.argv[3])
			s = True

		except:
			pass

	cw = CairoWindow(w, h, f, s)

	if not s:
		cw.connect("destroy", Gtk.main_quit)
		cw.show_all()
		cw.set_size_request(-1, 550)

		Gtk.main()
